﻿namespace SenenFernandez.SmartExtensionMethods
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Reflection;

    /// <summary>
    /// Extension Methods for Windows Presentation Foundation.
    /// </summary>
    public static class Linq
    {
        /// <summary>
        /// Recursive SelectMany.
        /// </summary>
        public static IEnumerable<TResult> SelectHierarchy<TResult>(this IEnumerable<TResult> source,
            Func<TResult, IEnumerable<TResult>> collectionSelector, Func<TResult, bool> predicate)
        {
            if (source == null)
            {
                yield break;
            }
            foreach (var item in source)
            {
                if (predicate(item))
                {
                    yield return item;
                }
                var childResults = SelectHierarchy(collectionSelector(item), collectionSelector, predicate);
                foreach (var childItem in childResults)
                {
                    yield return childItem;
                }
            }
        }

        /// <summary>
        /// Gets EXE Version
        /// </summary>
        public static string GetAssemblyVersion(this Assembly assembly)
        {
            var attributes = assembly.GetCustomAttributes(typeof (AssemblyFileVersionAttribute), false);
            return (attributes.Length > 0) ? ((AssemblyFileVersionAttribute) attributes[0]).Version : string.Empty;
        }

        /// <summary>
        /// Converts double number to FileSize.
        /// </summary>
        public static string ToFileSize<T>(this T value)
        {
            double bytes = value.ToDouble(0);
            var units = new List<string> {"Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"};
            double pow = Math.Floor((bytes > 0 ? Math.Log(bytes) : 0)/Math.Log(1024));
            pow = Math.Min(pow, units.Count - 1);
            double size = (double) bytes/Math.Pow(bytes, pow);
            return string.Format("{0} {1}", size.ToString(pow == 0 ? "F0" : "F2"), units[(int) pow]);
        }

        /// <summary>
        /// Gets Percent.
        /// </summary>
        public static double ToFreeSpacePercent(this long value, long total)
        {
            return Math.Round((value*100.0)/total, 2);
        }

        /// <summary>
        /// Perform a deep copy of the object.
        /// </summary>
        /// <returns>The copied object.</returns>
        public static T Clone<T>(T source)
        {
            using (var memoryStream = new MemoryStream())
            {
                var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                binaryFormatter.Serialize(memoryStream, source);
                memoryStream.Position = 0;
                return (T)binaryFormatter.Deserialize(memoryStream);
            }
        }

        /// <summary>
        /// Gets the result of the comparison of filtered values.
        /// </summary>
        public static bool IsMatch(this string filter, IEnumerable<string> values)
        {
            return string.IsNullOrWhiteSpace(filter) || values.Any(value => value.ToLower().Contains(filter.ToLower()));
        }

        /// <summary>
        /// Compare two objects preventing null values.
        /// </summary>
        public static bool AreDifferent<T>(this T value, T newValue)
        {
            return !EqualityComparer<T>.Default.Equals(value, newValue);
        }

        /// <summary>
        /// Load Embedded Assembly.
        /// </summary>
        public static Assembly LoadEmbeddedAssembly(this string name, string extension = ".dll")
        {
            var executingAssembly = Assembly.GetExecutingAssembly();
            var assemblyName = new AssemblyName(name);
            string path = string.Format("{0}{1}", assemblyName.Name, extension);
            if (!assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture))
            {
                path = string.Format(@"{0}\{1}", assemblyName.CultureInfo, path);
            }
            using (var stream = executingAssembly.GetManifestResourceStream(path))
            {
                if (stream == null)
                {
                    return null;
                }
                var assemblyRawBytes = new byte[stream.Length];
                stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
                return Assembly.Load(assemblyRawBytes);
            }
        }
    }
}